package com.insleton.admin.employee.dao;

import com.insleton.admin.employee.model.BatchTest;
import com.insleton.admin.employee.model.Employee;
import com.vteba.tx.dao.impl.HibernateDaoImpl;
import com.vteba.tx.jdbc.spring.SpringJdbcTemplate;
import jakarta.inject.Inject;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.NotNull;
import org.springframework.jdbc.core.BatchPreparedStatementSetter;
import org.springframework.jdbc.support.JdbcUtils;
import org.springframework.stereotype.Repository;

import java.sql.Connection;
import java.sql.Date;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.List;

/**
 * 测试 hibernate 6.5 的代码
 */
@Repository
public class EmployeeJpaDao extends HibernateDaoImpl<Employee, String> {

    private static final Logger LOGGER = LogManager.getLogger(EmployeeJpaDao.class);

    @Inject
    @Override
    public void setSpringJdbcTemplate(SpringJdbcTemplate springJdbcTemplate) {
        super.setSpringJdbcTemplate(springJdbcTemplate);
    }

    /**
     * 纯JDBC实现的批量操作，一般用于insert或update，性能最好
     *
     * @param objectArgsList SQL参数
     * @param insertSQL insert或update语句
     * @return 保存结果
     */
    public int[] batchUpdate(List<Object[]> objectArgsList, String insertSQL) {
        Connection conn = null;
        PreparedStatement ps = null;
        // 批次大小
        int batchSize = 5000;
        int size = objectArgsList.size();
        int[] result = new int[size]; // 有必要显示这个结果吗？
        try {
            conn = springJdbcTemplate.getConnection(); // 获取数据库连接
            ps = conn.prepareStatement(insertSQL);
            conn.setAutoCommit(false); // 取消自动提交

            int batch = 0; // 批次
            int count = 1; // 每批次处理的数据数
            for (Object[] objectArray : objectArgsList) {
                int s = 1;
                for (Object param : objectArray) {
                    ps.setObject(s++, param);
                }
                ps.addBatch();
                if (count % batchSize == 0) {
                    int[] bs = ps.executeBatch();
                    ps.clearBatch();
                    System.arraycopy(bs, 0, result, batch * batchSize, batchSize);
                    // 重置批次
                    count = 0; // 因为下一行会加1
                    ++batch;
                }
                ++count;
            }
            // 最后不够batchSize的，提交批量执行
            int[] ebs = ps.executeBatch();
            ps.clearBatch();
            System.arraycopy(ebs, 0, result, batch * batchSize, ebs.length);
            conn.commit(); // 所有语句都执行完毕后才手动提交sql语句
        } catch (SQLException e) {
            LOGGER.error("批量保存SQL异常", e);
            throw new RuntimeException("批量保存SQL异常", e);
        } finally {
            JdbcUtils.closeConnection(conn);
            JdbcUtils.closeStatement(ps);
        }
        return result;
    }

    /**
     * 批量操作（JdbcTemplate实现），一般用于insert和update。性能和纯JDBC相似
     *
     * @param insertSQL insert或update语句
     * @param objectArgsList sql参数
     * @return 更新的结果
     */
    public int[] batchUpdate(String insertSQL, List<Object[]> objectArgsList) {
        return springJdbcTemplate.batchUpdate(insertSQL, objectArgsList);
    }

    /**
     * JavaBean参数的批量操作（JdbcTemplate实现），一般用于insert和update。性能和纯JDBC相似
     *
     * @param insertSQL insert或update语句
     * @param batchTestList sql参数
     * @return 保存成功条数
     */
    public int[] batchSave(String insertSQL, List<BatchTest> batchTestList) {
        // 不要自己分批，JdbcTemplate内部已经分批了。自己分批性能反而低
        int[] size = springJdbcTemplate.batchUpdate(insertSQL, new BatchPreparedStatementSetter() {
            @Override
            public void setValues(@NotNull PreparedStatement ps, int i) throws SQLException {
                BatchTest test = batchTestList.get(i);
                ps.setString(1, test.getId());
                ps.setString(2, test.getName());
                ps.setInt(3, test.getAge());
                ps.setDouble(4, test.getSalary());
                // ps.setDate 只有日期部分，没有时间部分。setTimestamp才包含日期和时间
                ps.setTimestamp(5, new Timestamp(test.getCreateDate().getTime()));
                ps.setString(6, test.getRemark());
            }

            @Override
            public int getBatchSize() { // 这个批次大小，是决定上面setValues中i的值的最大范围的
                return batchTestList.size();
            }
        });
        return size;
    }
}
